using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace memcached
{
    /// <summary>
    /// Native win32 code.
    /// </summary>
    [System.Security.SuppressUnmanagedCodeSecurity]
    internal static class NativeMethods
    {
        /// <summary>
        /// The QueryPerformanceFrequency function retrieves the frequency of the high-resolution performance counter, if one exists. The frequency cannot change while the system is running.
        /// </summary>
        /// <param name="frequency">Pointer to a variable that receives the current performance-counter frequency, in counts per second. If the installed hardware does not support a high-resolution performance counter, this parameter can be zero.</param>
        /// <returns>If the installed hardware supports a high-resolution performance counter, the return value is nonzero.<br />
        /// If the function fails, the return value is zero. To get extended error information, call GetLastError. For example, if the installed hardware does not support a high-resolution performance counter, the function fails.
        /// </returns>
        /// <remarks>The frequency of the high-resolution performance counter is not the processor speed.</remarks>
        [DllImport("kernel32.dll", SetLastError=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool QueryPerformanceFrequency(out long frequency);

        /// <summary>
        /// The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter.
        /// </summary>
        /// <param name="lpPerformanceCount">Pointer to a variable that receives the current performance-counter value, in counts.</param>
        /// <returns>If the function succeeds, the return value is nonzero.<br />
        /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
        /// </returns>
        /// <remarks>On a multiprocessor machine, it should not matter which processor is called. However, you can get different results on different processors due to bugs in the BIOS or the HAL. To specify processor affinity for a thread, use the SetThreadAffinityMask function.
        /// </remarks>
        [DllImport("KERNEL32", SetLastError=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
    }

    /// <summary>
    /// Wrapper for high precision timer.
    /// </summary>
    /// <remarks>
    /// Example:
    /// <code>
    /// using (new PerformanceWrapper("Operation"))
    /// {
    ///     //operations
    /// }
    /// </code>
    /// </remarks>
    public sealed class PerformanceWrapper : IDisposable
    {
        private PerformanceWrapper() { }

        private PerformanceTimer m_tm;

        private string m_name;

        static PerformanceWrapper()
        {
            Init();
        }
        private static float Delta = 0;

        /// <summary>
        /// </summary>
        public static void Start() { }

        /// <summary>
        /// Simulate start.
        /// </summary>
        /// <param name="name">routine name</param>
        [System.Diagnostics.DebuggerStepThrough()]
        public PerformanceWrapper(string name)
        {
            Start(name);
        }

        /// <summary>
        /// Simulate stop.
        /// </summary>
        [System.Diagnostics.DebuggerStepThrough()]
        public void Dispose()
        {
            Stop();
            m_tm = null;
            GC.SuppressFinalize(this);
        }

        [Conditional("TRACE")]
        [System.Diagnostics.DebuggerStepThrough()]
        private static void Init()
        {
            string id = "Init";
            PerformanceTimer t = new PerformanceTimer();
            t.Start();
            using (new PerformanceWrapper(id)) { }
            t.Stop();
            //zmierz czas od start do stop
            PerformanceWrapper p = new PerformanceWrapper(id);
            p.Stop();
            Delta = p.m_tm.Seconds;
        }

        [Conditional("TRACE")]
        [System.Diagnostics.DebuggerStepThrough()]
        private void Start(string name)
        {
            System.Diagnostics.Trace.WriteLine(name);
            System.Diagnostics.Trace.Indent();
            m_name = name;
            m_tm = new PerformanceTimer();
            m_tm.Start();
        }

        [Conditional("TRACE")]
        [System.Diagnostics.DebuggerStepThrough()]
        private void Stop()
        {
            m_tm.Stop();
            float t = m_tm.Seconds - Delta;
            System.Diagnostics.Trace.Unindent();
            System.Diagnostics.Trace.WriteLine(String.Format("{0}: {1:N6}s", m_name, t));
            m_name = null;
        }
    }

    /// <summary>
    /// High precision timer. Object does not need any finalization.
    /// </summary>
    public sealed class PerformanceTimer
    {
        private long m_StartCount;
        private long m_StopCount;
        private long m_ElapsedCount;
        private static long m_Frequency;

        static PerformanceTimer()
        {
            NativeMethods.QueryPerformanceFrequency(out m_Frequency);
        }

        /// <summary>
        /// Start counting.
        /// </summary>
        public void Start()
        {
            NativeMethods.QueryPerformanceCounter(out m_StartCount);
        }

        /// <summary>
        /// Stop counting.
        /// </summary>
        public void Stop()
        {
            NativeMethods.QueryPerformanceCounter(out m_StopCount);
            m_ElapsedCount += m_StopCount - m_StartCount;
        }

        /// <summary>
        /// Reset timer.
        /// </summary>
        public void Reset()
        {
            m_ElapsedCount = 0;
        }

        /// <summary>
        /// Elapsed ticks.
        /// </summary>
        public long Elapsed
        {
            [System.Diagnostics.DebuggerStepThrough()]
            get { return m_ElapsedCount; }
        }

        /// <summary>
        /// Elapsed seconds.
        /// </summary>
        public float Seconds
        {
            get { return (float) m_ElapsedCount / (float) m_Frequency; }
        }

        /// <summary>
        /// Timer frequency.
        /// </summary>
        public static long Frequency
        {
            [System.Diagnostics.DebuggerStepThrough()]
            get { return m_Frequency; }
        }
    }
}